﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SharePoint;
using NUnit.Framework;
using PS.Practices.Provisioner.Test.Integration.ServiceLocator;
using PS.Practices.SiteBuilder.Features.Schema;
using PS.Practices.SiteBuilder.Features.Schema.DOM;
using PS.Practices.Provisioner.Provisioner.Schema.Logic;
using PS.Practices.Util;

namespace PS.Practices.Provisioner.Test.Integration
{
    [TestFixture]
    public class WebOperationTest
    {
        [TestFixtureSetUp]
        public void TestFixtureSetUp()
        {
            //Instantiate the web
            Site = new SPSite(Constants.TEST_SP_SITE_URL);
            //Web = Site.OpenWeb(Constants.TEST_SP_WEB_PATH);
            
            if(Site.AllWebs[Constants.TEST_SP_WEB_URL].Exists)
            {
                Site.AllWebs[Constants.TEST_SP_WEB_URL].Delete();
                //throw new Exception("Could not create integration testing web.");
            }

            var template = Site.OpenWeb().GetWebTemplate((uint)1033, "STS#1");
            Web = Site.AllWebs.Add(Constants.TEST_SP_WEB_URL, Constants.TEST_SP_WEB_NAME, "This web was automatically provisioned.", 1033,
                                                  template, false, false);

            //Set up the properties
            Dictionary<string,object> properties = new Dictionary<string, object>();
            
            //Dummy Users
            //TODO - this won't work on someone else's machine
            SPFieldUserValueCollection users = new SPFieldUserValueCollection();
            users.Add(new SPFieldUserValue(Web, @"1;#SP2010VM\administrator"));
            users.Add(new SPFieldUserValue(Web, @"127;#SP2010VM\spadmin"));

            properties.Add("Owners", users);
            

            // Grab the visitor(s)
            IServiceLocator locator = new OmWebOpServiceLocator(Web, properties);
            Visitor = locator.GetService<IWebOperationVisitor>();

            //De-Serialize the script
            string script = TestUtilities.GetTestScript("TestGrammar.xml");
            Root = DeSerializeScript(Web, script);
        }

        [TestFixtureTearDown]
        public void TestFixtureTearDown()
        {
            Site.Dispose();
            Web.Dispose();
            Visitor.Dispose();

            Site = null;
            Web = null;
            Visitor = null;
        }

        public ProvisionerRoot DeSerializeScript(SPWeb web, string script)
        {
            Extension[] exs = new Extension[0];
            ProvisionerRoot root = DeSerializer<ProvisionerRoot>.DeserializeFromString(script, exs);
            root.ReopenWebOnApplyWebTemplate = true;
            return root;
        }

        public IWebOperationVisitor Visitor { get; set; }

        public SPWeb Web { get; set; }

        public SPSite Site { get; set; }

        public ProvisionerRoot Root { get; set; }

        //TODO add functionality for updating the taxonomy?

        [Test]
        public void ProvisionerRootVisitTest()
        {
            Visitor.Visit(Root);

            //TODO Investigate TypeMoch ability to moch parent and/or root web's master pages
            switch (Root.CustomMasterUrl)
            {
                case ("{{None}}"):
                    //Nothing should happen
                    break;
                case ("{{Parent}}"):
                    using (SPWeb parentWeb = Web.Site.OpenWeb(Web.ParentWebId))
                    {
                        Assert.AreEqual(parentWeb.CustomMasterUrl, Web.CustomMasterUrl, "CustomMasterUrl {{Parent}}");
                    }
                    break;
                case ("{{Root}}"):
                    using (SPWeb root = Web.Site.OpenWeb(Web.Site.ServerRelativeUrl))
                    {
                        Assert.AreEqual(root.CustomMasterUrl, Web.CustomMasterUrl, "CustomMasterUrl {{Root}}");
                    }
                    break;
                default:
                    Assert.AreEqual(Web.CustomMasterUrl, Root.CustomMasterUrl, "CustomMasterUrl");
                    break;
            }

            switch (Root.MasterUrl)
            {
                case ("{{None}}"):
                    //Nothing should happen
                    break;
                case ("{{Parent}}"):
                    using (SPWeb parentWeb = Web.Site.OpenWeb(Web.ParentWebId))
                    {
                        Assert.AreEqual(parentWeb.MasterUrl, Web.MasterUrl, "MasterUrl {{Parent}}");
                    }
                    break;
                case ("{{Root}}"):
                    using (SPWeb root = Web.Site.OpenWeb(Web.Site.ServerRelativeUrl))
                    {
                        Assert.AreEqual(root.MasterUrl, Web.MasterUrl, "MasterUrl {{Root}}");
                    }
                    break;
                default:
                    Assert.AreEqual(Web.MasterUrl, Root.MasterUrl, "MasterUrl");
                    break;
            }
        }

        /// <summary>
        /// FixUpWebAcl processing
        /// </summary>
        /// <remarks>
        /// Todo - Testing Logic for "BreakAndCopy"
        /// TODO - Testing logic for Group Deletion
        /// </remarks>
        [Test]
        public void FixUpWebAclVisitTest()
        {
            FixupWebACL fwa = Root.Operations.OfType<FixupWebACL>().First();

            Visitor.Visit(fwa);

            /* InheritTopNav */
            Assert.AreEqual(fwa.InheritTopNav, Web.Navigation.UseShared);

            /* RespectInheritance, RoleAssignments */
            Assert.IsFalse(Web.HasUniqueRoleDefinitions, "HasUniqueRoleDefinitions");

            /* SyncGroupQuickLaunch */
            /* This looks commented out */
        }

        /// <summary>
        /// SiteGroup processing
        /// </summary>
        /// <dependencies>
        /// To add groups using this method we have to break inheritance.  Inheritance can be broken in FixUpWebAcl
        /// </dependencies>
        /// <remarks>
        /// TODO - logic for each settings of the SiteGroup Node (currently only verifies Name)
        /// </remarks>
        [Test]
        public void SiteGroupVisitTest()
        {
            FixupWebACL fwa = Root.Operations.OfType<FixupWebACL>().First();
            IEnumerable<SiteGroup> groups = fwa.Operations.OfType<SiteGroup>();

            foreach (SiteGroup g in groups)
            {
                Web.AllowUnsafeUpdates = true; //Only need this when running all tests at once.  No idea why
                Visitor.Visit(g);
                Web.AllowUnsafeUpdates = false;

                string groupName = ParserUtil.ReplaceLiveFields(g.Name, Web);
                SPGroup group = Web.SiteGroups[groupName];

                Assert.AreEqual(group.AllowMembersEditMembership, g.AllowMembersEditMembership, "Group.AllowMembersEditMembership");
                Assert.AreEqual(group.AllowRequestToJoinLeave, g.AllowRequestToJoinLeave, "Group.AllowRequestToJoinLeave");

                Assert.NotNull(Web.SiteGroups[ParserUtil.ReplaceLiveFields(g.Name, Web)]);
            }
        }

        /// <summary>
        /// AssociateSiteGroup processing
        /// </summary>
        /// <remarks>
        /// Todo - Assert statements
        /// </remarks>
        [Test]
        public void AssociateSiteGroupVisitTest()
        {
            //Groups must exist before they can be associated
            RunSiteGroupVisit();

            FixupWebACL fwa = Root.Operations.OfType<FixupWebACL>().First();
            IEnumerable<AssociateSiteGroup> groups = fwa.Operations.OfType<AssociateSiteGroup>();

            foreach(AssociateSiteGroup a in groups)
            {
                Visitor.Visit(a);
            }
        }

        /// <summary>
        /// Test for ProvisionLists
        /// </summary> 
        [Test]
        public void ProvisionListsTest()
        {
            ProvisionLists pl = Root.Operations.OfType<ProvisionLists>().First();

            Visitor.Visit(pl);

            foreach(PS.Practices.SiteBuilder.Features.Schema.DOM.List list in pl.Lists)
            {
                SPList provisionedList = Web.Lists.TryGetList(list.Title);



                Assert.IsNotNull(provisionedList, "ProvisionLists");
                Assert.AreEqual((int)provisionedList.BaseTemplate, list.Template, "ListTemplate");
            }
        }

        /// <summary>
        /// Provision members to ACL groups
        /// </summary>
        [Test]
        public void AddMembersToSiteGroupTest()
        {
            //Groups must exist before they can be added to
            RunSiteGroupVisit();

            FixupWebACL fwa = Root.Operations.OfType<FixupWebACL>().First();
            IEnumerable<AddMembersToSiteGroup> addMembersList = fwa.Operations.OfType<AddMembersToSiteGroup>();
            
            foreach (AddMembersToSiteGroup amtsg in addMembersList)
            {
                Visitor.Visit(amtsg);

                SPFieldUserValueCollection col = Visitor.ReplaceLiveFields(amtsg.Members) as SPFieldUserValueCollection;
                string groupName = Visitor.ReplaceLiveFields(amtsg.GroupName) as string;
                var group = Web.SiteGroups[groupName];

                foreach(var val in col)
                {
                    SPUser user = null;
                    try
                    {
                        user = group.Users[val.User.LoginName];
                    }
                    catch(Exception ex){}

                    Assert.NotNull(user, "Group: " + group.Name + " does not contain user: " + val.User.Name);
                }
            }
        }
            

        //TODO xml node object get-able by properties.  Instantiate properties in testfixturesetup
        private void RunFixUpWebAclVisit()
        {
            FixupWebACL fwa = Root.Operations.OfType<FixupWebACL>().First();
            Visitor.Visit(fwa);
        }

        private void RunSiteGroupVisit()
        {
            RunFixUpWebAclVisit();
            RunAssociateSiteGroupVisit();

            FixupWebACL fwa = Root.Operations.OfType<FixupWebACL>().First();
            IEnumerable<SiteGroup> groups = fwa.Operations.OfType<SiteGroup>();

            foreach (SiteGroup g in groups)
            {
                Visitor.Visit(g);
            }
        }

        private void RunAssociateSiteGroupVisit()
        {
            FixupWebACL fwa = Root.Operations.OfType<FixupWebACL>().First();
            IEnumerable<AssociateSiteGroup> groups = fwa.Operations.OfType<AssociateSiteGroup>();

            foreach (AssociateSiteGroup a in groups)
            {
                Visitor.Visit(a);
            }
        }

        private void RunPrincipalRoleVisit()
        {
            FixupWebACL fwa = Root.Operations.OfType<FixupWebACL>().First();
            IEnumerable<PrincipalRole> roles = fwa.Operations.OfType<PrincipalRole>();

            foreach(PrincipalRole pr in roles)
            {
                Visitor.Visit(pr);
            }
        }
    }
}
